﻿/*
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.DirectX;
using Microsoft.DirectX.Direct3D;
using System.Xml;

namespace Daidalos.UI
{
    /// <summary>
    /// 
    /// </summary>
    public class GuiManager : Patterns.SingletonManager<GuiManager>
    {

        #region Attributes
        /// <summary>
        /// 
        /// </summary>
        private List<Sprite> quads;

        /// <summary>
        /// 
        /// </summary>
        private List<Sprite> fontQuads;

        /// <summary>
        /// 
        /// </summary>
        private List<ControlNode> controlNodes;

        /// <summary>
        /// 
        /// </summary>
        private List<Control> controls;

        /// <summary>
        /// 
        /// </summary>
        private VertexBuffer vb = null;

        /// <summary>
        /// 
        /// </summary>
        private Texture texture = null;

        /// <summary>
        /// 
        /// </summary>
        private ImageInformation imageInfo;

        /// <summary>
        /// 
        /// </summary>
        private string textureFilename;

        /// <summary>
        /// 
        /// </summary>
        private string fntFile;

        /// <summary>
        /// 
        /// </summary>
        private string fontImage;

        /// <summary>
        /// 
        /// </summary>
        private string searchName;

        /// <summary>
        /// 
        /// </summary>
        private bool inCallback;

        /// <summary>
        /// 
        /// </summary>
        private bool dirtyBuffer;

        /// <summary>
        /// 
        /// </summary>
        private bool panelDragging;

        /// <summary>
        /// 
        /// </summary>
        private int openComboBox;

        /// <summary>
        /// 
        /// </summary>
        private int activeEditBox;

        /// <summary>
        /// 
        /// </summary>
        private Font.CharacterSet font = null;

        /// <summary>
        /// 
        /// </summary>
        private const int maxVertices = 4096;
        #endregion

        #region Accessors
        /// <summary>
        /// 
        /// </summary>
        public List<Sprite> Quads
        {
            get 
            {
                return quads; 
            }
            set 
            {
                quads = value; 
            }
        }
        
        /// <summary>
        /// 
        /// </summary>
        public List<Sprite> FontQuads
        {
            get 
            {
                return fontQuads; 
            }
            set 
            {
                fontQuads = value; 
            }
        }
        
        /// <summary>
        /// 
        /// </summary>
        public List<ControlNode> ControlNodes
        {
            get 
            {
                return controlNodes; 
            }
            set 
            {
                controlNodes = value; 
            }
        }
        
        /// <summary>
        /// 
        /// </summary>
        public List<Control> Controls
        {
            get 
            {
                return controls; 
            }
            set 
            {
                controls = value; 
            }
        }
        
        /// <summary>
        /// 
        /// </summary>
        public VertexBuffer Vb
        {
            get 
            {
                return vb; 
            }
            set 
            {
                vb = value; 
            }
        }

        /// <summary>
        /// 
        /// </summary>
        public Texture Texture
        {
            get 
            {
                return texture; 
            }
            set 
            {
                texture = value; 
            }
        }
        
        /// <summary>
        /// 
        /// </summary>
        public ImageInformation ImageInfo
        {
            get 
            {
                return imageInfo; 
            }
            set
            {
                imageInfo = value; 
            }
        }
        
        /// <summary>
        /// 
        /// </summary>
        public string TextureFilename
        {
            get 
            {
                return textureFilename; 
            }
            set 
            {
                textureFilename = value; 
            }
        }
        
        /// <summary>
        /// 
        /// </summary>
        public string FntFile
        {
            get 
            {
                return fntFile; 
            }
            set 
            {
                fntFile = value; 
            }
        }
        
        /// <summary>
        /// 
        /// </summary>
        public string FontImage
        {
            get 
            {
                return fontImage; 
            }
            set 
            {
                fontImage = value; 
            }
        }
        
        /// <summary>
        /// 
        /// </summary>
        public string SearchName
        {
            get 
            {
                return searchName; 
            }
            set 
            {
                searchName = value; 
            }
        }
        
        /// <summary>
        /// 
        /// </summary>
        public bool InCallback
        {
            get 
            {
                return inCallback; 
            }
            set 
            {
                inCallback = value; 
            }
        }
        
        /// <summary>
        /// 
        /// </summary>
        public bool DirtyBuffer
        {
            get 
            {
                return dirtyBuffer; 
            }
            set 
            { 
                dirtyBuffer = value; 
            }
        }
        
        /// <summary>
        /// 
        /// </summary>
        public bool PanelDragging
        {
            get 
            {
                return panelDragging; 
            }
            set 
            {
                panelDragging = value; 
            }
        }
        
        /// <summary>
        /// 
        /// </summary>
        public int OpenComboBox
        {
            get 
            {
                return openComboBox; 
            }
            set 
            {
                openComboBox = value; 
            }
        }
        
        /// <summary>
        /// 
        /// </summary>
        public int ActiveEditBox
        {
            get 
            {
                return activeEditBox; 
            }
            set 
            {
                activeEditBox = value; 
            }
        }
        
        /// <summary>
        /// 
        /// </summary>
        public Font.CharacterSet Font
        {
            get 
            {
                return font; 
            }
            set 
            {
                font = value; 
            }
        }
        
        /// <summary>
        /// 
        /// </summary>
        public int MaxVertices
        {
            get 
            {
                return maxVertices; 
            }
        }
        #endregion

        #region Delegates
        /// <summary>
        /// 
        /// </summary>
        /// <param name="controlID"></param>
        /// <param name="data"></param>
        public delegate void ControlDelegate(int controlID, object data);

        /// <summary>
        /// 
        /// </summary>
        private ControlDelegate onControl;
        #endregion

        #region Methods
        /// <summary>
        ///     Loads the GUI configuration and all elements
        ///     from an XML file
        /// </summary>
        /// <param name="xmlfile">
        ///     The XML File to load from
        /// </param>
        private void ParseXml(string xmlfile)
        {
            XmlTextReader reader = new XmlTextReader(Utilities.Utility.GetMediaFile(xmlfile));

            if (reader == null)
            {
                throw new Exception("Unable to retrieve GUI Configfile");
            }

            reader.WhitespaceHandling = WhitespaceHandling.None;

            while (reader.Read())
            {
                if (reader.NodeType == XmlNodeType.Element)
                {
                    if (reader.Name == "Gui")
                    {
                        for (int i = 0; i < reader.AttributeCount; i++)
                        {
                            reader.MoveToAttribute(i);
                            if (reader.Name == "ImageFile")
                            {
                                TextureFilename = Utilities.Utility.GetMediaFile(reader.Value);
                                //ImageInfo = Texture.
                            }
                            else if (reader.Name == "FntFile")
                            {
                                FntFile = reader.Value;
                            }
                            else if (reader.Name == "FontImage")
                            {
                                FontImage = reader.Value;
                            }
                        }
                    }
                    else if (reader.Name == "Control")
                    {
                        ControlNode controlNode = new ControlNode();
                        for (int i = 0; i < reader.AttributeCount; i++)
                        {
                            reader.MoveToAttribute(i);
                            if (reader.Name == "Name")
                            {
                                controlNode.Name = reader.Value;
                            }
                        }

                        while (reader.NodeType != XmlNodeType.EndElement)
                        {
                            reader.Read();
                            if ((reader.NodeType == XmlNodeType.Element) &&
                                (reader.Name == "Image"))
                            {
                                ImageNode imageNode = new ImageNode();
                                for (int i = 0; i < reader.AttributeCount; i++)
                                {
                                    reader.MoveToAttribute(i);
                                    if (reader.Name == "Name")
                                    {
                                        imageNode.Name = reader.Value;
                                    }
                                    else if (reader.Name == "X")
                                    {
                                        imageNode.Rectangle.X = reader.ReadContentAsFloat();
                                    }
                                    else if (reader.Name == "Y")
                                    {
                                        imageNode.Rectangle.Y = reader.ReadContentAsFloat();
                                    }
                                    else if (reader.Name == "Width")
                                    {
                                        imageNode.Rectangle.Width = reader.ReadContentAsFloat();
                                    }
                                    else if (reader.Name == "Height")
                                    {
                                        imageNode.Rectangle.Height = reader.ReadContentAsFloat();
                                    }
                                    else if (reader.Name == "Color")
                                    {
                                        imageNode.Color = StringToColor(reader.Value);
                                    }
                                }
                                controlNode.Images.Add(imageNode);
                            }
                        }
                        ControlNodes.Add(controlNode);
                    }
                }
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="hexstring"></param>
        /// <returns></returns>
        private System.Drawing.Color StringToColor(string hexstring)
        {
            if (hexstring.IndexOf("0x") >= 0)
            {
                hexstring = hexstring.Remove(0, 2);
            }

            System.Globalization.NumberStyles style =
                System.Globalization.NumberStyles.AllowHexSpecifier;

            int alpha = int.Parse(hexstring.Substring(0, 2), style);
            int red = int.Parse(hexstring.Substring(2, 2), style);
            int green = int.Parse(hexstring.Substring(4, 2), style);
            int blue = int.Parse(hexstring.Substring(6, 2), style);
            return System.Drawing.Color.FromArgb(alpha, red, green, blue);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="device"></param>
        public void OnCreateDevice(Device device)
        {
            // Empty
        }

        /// <summary>
        /// 
        /// </summary>
        public void OnDestroyDevice()
        {
            if (Texture != null)
            {
                Texture.Dispose();
                Texture = null;
            }

            if (Font != null)
            {
            }
        }

        /// <summary>
        /// 
        /// </summary>
        public void OnLostDevice()
        {
            if (Vb != null)
            {
                Vb.Dispose();
                Vb = null;
            }
        }

        /// <summary>
        /// 
        /// </summary>
        public void OnResetDevice(Device device)
        {
            Vb = new VertexBuffer(device, MaxVertices * CustomVertex.TransformedColoredTextured.StrideSize,
                Usage.Dynamic | Usage.WriteOnly, CustomVertex.TransformedColoredTextured.Format,
                Pool.Default);
            BuildQuadList();
            UpdateBuffer();
            if (Font != null)
            {
            }
        }

        /// <summary>
        /// 
        /// </summary>
        public void Clear()
        {
            Quads.Clear();
            FontQuads.Clear();
            ControlNodes.Clear();
            Controls.Clear();
        }

        /// <summary>
        /// /
        /// </summary>
        /// <param name="id"></param>
        /// <param name="position"></param>
        /// <param name="size"></param>
        public void CreatePanel(int id, System.Drawing.PointF position,
            System.Drawing.SizeF size)
        {
            CheckUniqueID(id);
            SearchName = "Panel";
            ControlNode node = ControlNodes.Find(HasSearchName);
            if (node == null)
            {
                throw new Exception("Unable to find control node for type Panel");
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="id"></param>
        /// <param name="panelID"></param>
        /// <param name="position"></param>
        /// <param name="size"></param>
        /// <param name="text"></param>
        /// <param name="fontSize"></param>
        /// <param name="maxLength"></param>
        /// <param name="textColor"></param>
        public void CreateEditBox(int id, int panelID, System.Drawing.PointF position,
            System.Drawing.SizeF size, string text, float fontSize,
            int maxLength, ColorValue textColor)
        {
            CheckUniqueID(id);
            SearchName = "EditBox";
            ControlNode node = ControlNodes.Find(HasSearchName);
            if (node == null)
            {
                throw new Exception("Unable to find control node for type EditBox");
            }
            EditBox c = new EditBox(id, new System.Drawing.RectangleF(position, size),
                text, fontSize, maxLength, textColor, Font, node, ImageInfo);
            AddControl(panelID, c);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="id"></param>
        private void CheckUniqueID(int id)
        {
            foreach (Control c in Controls)
            {
                if (c.Id == id)
                    throw new Exception("Control ID: " + id + " is already in use!");
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="controlID"></param>
        /// <param name="text"></param>
        /// <param name="data"></param>
        public void AddListableItem(int controlID, string text, object data)
        {
            Control c = Controls[controlID];
            if (!(c is ListBox))
            {
                throw new Exception("Error: Tried to add ListableItem to non-listable Control");
            }
            (c as ListBox).AddItem(text, data);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="panelID"></param>
        /// <param name="c"></param>
        public void AddControl(int panelID, Control c)
        {
            if (panelID == 0)
            {
                if (Controls.Count > 0)
                {
                    c.ZDepth = Controls[0].ZDepth + 1;
                }
                else
                {
                    c.ZDepth = 1;
                }
            }
            else
            {
                AttachControlToPanel(panelID, c);
            }
            c.OnControl += new ControlDelegate(onControl);
            Controls.Add(c);
            SortControls();
            //BuildQuadList();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="panelID"></param>
        /// <param name="c"></param>
        public void AttachControlToPanel(int panelID, Control c)
        {
            Control p = Controls[panelID];
            if (!(p is Panel))
            {
                throw new Exception("Error: Tried to attach Control to non-panel Control!");
            }
            c.PanelID = panelID;
            c.ZDepth = p.ZDepth;
            //(p as Panel).NumControls++;
            c.Position += new System.Drawing.SizeF(p.Position.X, p.Position.Y);
        }

        /// <summary>
        /// 
        /// </summary>
        private void BuildQuadList()
        {
            Quads.Clear();
            FontQuads.Clear();
            foreach (Control control in Controls)
            {
                control.StartVertex = Quads.Count * 6;
                Quads.AddRange(control.Quads);
                control.FontStartVertex = Quads.Count * 6;
                Quads.AddRange(control.FontQuads);
            }
        }

        /// <summary>
        /// 
        /// </summary>
        private void UpdateBuffer()
        {
            if (InCallback)
                return;

            GraphicsStream data =
                Vb.Lock(0, 6 * Quads.Count, LockFlags.Discard);

            foreach (Sprite sprite in Quads)
            {
                data.Write(sprite.Vertices);
            }
            Vb.Unlock();
            DirtyBuffer = false;
        }

        /// <summary>
        /// 
        /// </summary>
        public void Render()
        {
            foreach(Control con in Controls)
            {
            }
        }

        /// <summary>
        /// 
        /// </summary>
        private void SortControls()
        {
            ControlSorter sorter = new ControlSorter();
            Controls.Sort(sorter);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="node"></param>
        /// <returns></returns>
        private bool HasSearchName(ControlNode node)
        {
            return (node.Name == SearchName);
        }
        #endregion
    }
}
